#include <stdio.h>
#include <ctype.h>
#include <math.h>

#define ATOMINFO_C__
#include "atominfo.h"
#undef ATOMINFO_C__


void AtomInfoStripLabel(const char *Label, int Exact, char *buf, int mbuf)
{
  int         i;
  const char  *digit;


  mbuf--;

  while (*Label && isspace(*Label)) Label++;

  digit = NULL;

  for (i = 0; i < mbuf && *Label; Label++)
  {
    if (isspace(*Label))
      break;

    if (isdigit(*Label))
    {
      if (digit) break;
      digit = Label;
    }
    else if (*Label == '+' || *Label == '-')
    {
      if (i + 1 < mbuf)
      {
        if (digit == NULL)
            digit = "1";

        buf[i++] = *digit;
      }

      buf[i++] = *Label++;

      break;
    }
    else if (digit)
      break;
    else
      buf[i++] = toupper(*Label);
  }

  if (Exact && *Label && ! isspace(*Label))
    i = 0;

  buf[i] = '\0';
}


const T_PSE *FindInPSE(const char *Label, int Exact)
{
  int          i, m;
  char         buf[3];
  const T_PSE  *ePSE, *mPSE;


  AtomInfoStripLabel(Label, Exact, buf, sizeof buf / sizeof (*buf));

  if (buf[0] == '\0')
    return NULL;

  m    = 0;
  mPSE = NULL;

  for (ePSE = PSE; ePSE->Z; ePSE++)
  {
    for (i = 0; buf[i] && ePSE->Symbol[i]; i++)
      if (buf[i] != toupper(ePSE->Symbol[i])) break;

    if (buf[i] == '\0' && ePSE->Symbol[i] == '\0')
      return ePSE;

    if (i == 1 && isalpha(ePSE->Symbol[1]))
      i = 0;

    if (i > m)
    {
      m    = i;
      mPSE = ePSE;
    }
  }

  if (Exact)
    return NULL;

  return mPSE;
}


const T_SF_IT92_CAA *FindSF_IT92_CAA(const char *Label, int Exact)
{
  int                  i, m;
  char                 buf[6];
  const T_SF_IT92_CAA  *SFC, *mSFC;


  AtomInfoStripLabel(Label, Exact, buf, sizeof buf / sizeof (*buf));

  if (buf[0] == '\0')
    return 0;

  m    = 0;
  mSFC = NULL;

  for (SFC = SF_IT92_CAA; SFC->Label; SFC++)
  {
    for (i = 0; buf[i] && SFC->Label[i]; i++)
      if (buf[i] != toupper(SFC->Label[i])) break;

    if (buf[i] == '\0' && SFC->Label[i] == '\0')
    {
      if (SFC->c) return SFC;

      for (i = 0; i < (sizeof SFC->b / sizeof (*SFC->b)); i++)
        if (SFC->b[i]) return SFC;

      for (i = 0; i < (sizeof SFC->a / sizeof (*SFC->a)); i++)
        if (SFC->a[i]) return SFC;

      return NULL;
    }

    if (i == 1 && isalpha(SFC->Label[1]))
      i = 0;

    if (i > m && ! isdigit(SFC->Label[i - 1]))
    {
      m    = i;
      mSFC = SFC;
    }
  }

  if (Exact)
    return NULL;

  return mSFC;
}


double CalcSF_IT92_CAA(const T_SF_IT92_CAA *CAA, double stol2)
{
  int     i;
  double  sf;


  sf = CAA->c;

  for (i = 0; i < (sizeof CAA->a / sizeof (*CAA->a)); i++)
    sf += CAA->a[i] * exp(-CAA->b[i] * stol2);

  return sf;
}


const T_SF_WK95_CAA *FindSF_WK95_CAA(const char *Label, int Exact)
{
  int                  i, m;
  char                 buf[6];
  const T_SF_WK95_CAA  *SFC, *mSFC;


  AtomInfoStripLabel(Label, Exact, buf, sizeof buf / sizeof (*buf));

  if (buf[0] == '\0')
    return 0;

  m    = 0;
  mSFC = NULL;

  for (SFC = SF_WK95_CAA; SFC->Label; SFC++)
  {
    for (i = 0; buf[i] && SFC->Label[i]; i++)
      if (buf[i] != toupper(SFC->Label[i])) break;

    if (buf[i] == '\0' && SFC->Label[i] == '\0')
    {
      if (SFC->c) return SFC;

      for (i = 0; i < (sizeof SFC->b / sizeof (*SFC->b)); i++)
        if (SFC->b[i]) return SFC;

      for (i = 0; i < (sizeof SFC->a / sizeof (*SFC->a)); i++)
        if (SFC->a[i]) return SFC;

      return NULL;
    }

    if (i == 1 && isalpha(SFC->Label[1]))
      i = 0;

    if (i > m && ! isdigit(SFC->Label[i - 1]))
    {
      m    = i;
      mSFC = SFC;
    }
  }

  if (Exact)
    return NULL;

  return mSFC;
}


double CalcSF_WK95_CAA(const T_SF_WK95_CAA *CAA, double stol2)
{
  int     i;
  double  sf;


  sf = CAA->c;

  for (i = 0; i < (sizeof CAA->a / sizeof (*CAA->a)); i++)
    sf += CAA->a[i] * exp(-CAA->b[i] * stol2);

  return sf;
}


const T_AtomRadius *FindAtomRadius(const char *Label, int Exact)
{
  int                 i, m;
  char                buf[5];
  const T_AtomRadius  *LAR, *mLAR;


  AtomInfoStripLabel(Label, Exact, buf, sizeof buf / sizeof (*buf));

  if (buf[0] == '\0')
    return NULL;

  m    = 0;
  mLAR = NULL;

  for (LAR = ListAtomRadius; LAR->Label; LAR++)
  {
    for (i = 0; buf[i] && LAR->Label[i]; i++)
      if (buf[i] != toupper(LAR->Label[i])) break;

    if (buf[i] == '\0' && LAR->Label[i] == '\0')
      return LAR;

    if (i == 1 && isalpha(LAR->Label[1]))
      i = 0;

    if (i > m && ! isdigit(LAR->Label[i - 1]))
    {
      m    = i;
      mLAR = LAR;
    }
  }

  if (Exact)
    return NULL;

  return mLAR;
}


const T_ChXrayWaveLength *ChXrayWaveLengthOf(const char *Label)
{
  const char                *s, *t;
  const T_ChXrayWaveLength  *cxw;


  for (cxw = ListChXrayWaveLengths; cxw->Label; cxw++)
  {
    for (s = cxw->Label, t = Label; *s || *t; s++, t++)
      if (toupper(*s) != toupper(*t)) break;

    if (! (*s || *t)) return cxw;
  }

  return NULL;
}


const T_NeutronBondSL_NN92 *FindNeutronBondSL_NN92(const char *Label,
                                                   int Exact)
{
  int                 i, m;
  char                buf[3];
  const T_NeutronBondSL_NN92  *NBSL, *mNBSL;


  AtomInfoStripLabel(Label, Exact, buf, sizeof buf / sizeof (*buf));

  if (buf[0] == '\0')
    return NULL;

  m    = 0;
  mNBSL = NULL;

  for (NBSL = ListNeutronBondSL_NN92; NBSL->Symbol; NBSL++)
  {
    for (i = 0; buf[i] && NBSL->Symbol[i]; i++)
      if (buf[i] != toupper(NBSL->Symbol[i])) break;

    if (buf[i] == '\0' && NBSL->Symbol[i] == '\0')
      return NBSL;

    if (i == 1 && isalpha(NBSL->Symbol[1]))
      i = 0;

    if (i > m && ! isdigit(NBSL->Symbol[i - 1]))
    {
      m    = i;
      mNBSL = NBSL;
    }
  }

  if (Exact)
    return NULL;
  return mNBSL;
}
